#ifndef __PARSE_BASE_NODES_H__
#define __PARSE_BASE_NODES_H__

#include "SmartPointer.h"

#include <climits>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <sstream>

struct ClassDeclarationNode;
struct TypeMeta {
	enum TypeCategory {
		Error = 0x0,
		Boolean = 0x1, Char = 0x2, Byte = 0x4, Short = 0x8, Int = 0x10, String = 0x20, Reference = 0x40, Array = 0x80,
		BooleanArray = Boolean | Array, CharArray = Char | Array, ByteArray = Byte | Array, ShortArray = Short | Array, IntArray = Int | Array, StringArray = String | Array, ReferenceArray = Reference | Array,
		Void = 0x200, Null = 0x400
	};

	TypeMeta() : category(Error), reference(0x0) {}
	TypeMeta(TypeCategory c, ClassDeclarationNode *r) : category(c), reference(r) {}

	static ClassDeclarationNode *string_declaration;

	TypeCategory category;
	ClassDeclarationNode *reference;
};



struct NodeVisitor;
struct ParentNode;
struct LeafNode;

struct ParseNode {
	ParseNode();
	virtual ~ParseNode();

	virtual std::string Type() { return "Node"; }
	virtual bool IsParent() { return false; }

	virtual void Accept(NodeVisitor *v) = 0;

	ParentNode *parent;

	static ParentNode* SetType(std::string, ParentNode *);
	static ParseNode* SetType(std::string, LeafNode *);

	static const std::map<std::string, ParseNode*> _typeMapping;
protected:
	virtual ParentNode* Convert(ParentNode *);
	virtual LeafNode* Convert(LeafNode *);
};

struct ParentNode : ParseNode {
	ParentNode();
	ParentNode(unsigned int size);
	~ParentNode();

	bool IsParent() { return true; }

	std::vector<SP<ParseNode> > children;

	SP<ParseNode>& operator[](unsigned int i);
	ParseNode *operator[](std::string s);

	void Accept(NodeVisitor*);
	void CascadeAccept(NodeVisitor*);

	void DeleteChildren();
	void DeleteOfType(std::string);
	void DeleteLeafs();
	void FlattenOn(std::string, bool = true);
	bool HasChildOfType(std::string);
	void ReplaceMeWith(ParseNode *);
protected:
	std::vector<SP<ParseNode> >::iterator FindChild(std::string);
};

struct LeafNode : ParseNode {
	LeafNode(std::string type, std::string lexeme);
	std::string type, lexeme;
	std::string Type() { return type; }

	void Accept(NodeVisitor*);
};

struct DeclarationNode : ParentNode {
	enum Namespace { Package, Type, Method, Expression };

	DeclarationNode() : index(INT_MAX), label(std::string()) {}

	std::string identifier;
	ClassDeclarationNode *containing_class;
	void SetIdentifier();
	int index;
	std::string label;

	//Returns the index modified in some way to product a relative (to the whatever the label is) offset for that index. Base implementation is index * 4 => position * bytes
	virtual int Offset() { return index * 4; }

	//Create a deference string that uses the label plus the Offset(). Base implementation: (EG. "[java.lang.Object+4]")
	virtual std::string DereferenceBrackets() {
		std::stringstream location_stream;
		location_stream << "[" << label << "+" << Offset() << "]";
		return location_stream.str();
	}
};

struct compare_decls {
	bool operator() (const DeclarationNode *d1, const DeclarationNode *d2) const {
		return d1->identifier < d2->identifier;
	}
};

struct ModifiedNode : DeclarationNode {
	enum AccessModifiers { Public = 0x1, Protected = 0x2 };
	enum Modifiers { Abstract = 0x1, Final = 0x2, Static = 0x4, Native = 0x8 };

	ModifiedNode() : access(0x0), modifier(0x0) {}
	void CollectModifiers();
	bool CheckModifiers(unsigned char access, unsigned char modifiers);
	unsigned char access, modifier;
};


struct TypedNode : ModifiedNode {
	TypedNode() : type_package_name(std::string()) {}

	void FillType();

	std::string type_package_name;
	std::string type_name;

	TypeMeta type_meta;
private:
	void GetTypeNameComponents();
	void GetTypeCategory();
};

struct EnvironmentNode {
	EnvironmentNode() : next_environment(0x0) {}

	EnvironmentNode *next_environment;
	std::map<DeclarationNode::Namespace, std::set<TypedNode*, compare_decls> > elements_in_scope;
	TypedNode* Get(DeclarationNode::Namespace n, std::string id) {
		//find this namespace in the environment
		std::map<DeclarationNode::Namespace, std::set<TypedNode*, compare_decls> >::iterator itn = elements_in_scope.find(n);
		if (itn == elements_in_scope.end()) return 0x0;
		//find this declaration in this namespace
		TypedNode temp;
		temp.identifier = id;
		std::set<TypedNode*, compare_decls>::iterator itd = itn->second.find(&temp);
		if (itd == itn->second.end()) return 0x0;

		SP<TypedNode> sp = *itd;
		return sp();
	}
};

struct VariableNode : TypedNode {
	VariableNode() : TypedNode(), underlying_type_reference(0x0) {}

	ClassDeclarationNode *underlying_type_reference;
};
#endif